#include "EntityManager.h"
#include "ParticleManager.h"
#include "EventManager.h"
#include "LevelManager.h"
#include "Shielder.h"
#include "Game.h"
#include "MessageSystem.h"
#include "MainMenuState.h"
#include "CSGD_XAudio2.h"
#include <cassert>

CEntityManager* CEntityManager::s_pInstance = nullptr;
/////////////////////////////////////////////////////////////////////////////
// Function		: CEntityManager
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
CEntityManager::CEntityManager(void)
{
	m_bIterating = false;

	CEventManager::GetInstance()->RegisterListener( "MISSILE", this );
}
/////////////////////////////////////////////////////////////////////////////
// Function		: ~CEntityManager
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
CEntityManager::~CEntityManager(void)
{
	Clear();

	CEventManager::GetInstance()->UnregisterListener( "MISSILE", this );
}
/////////////////////////////////////////////////////////////////////////////
// Function		: GetInstance
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
CEntityManager* CEntityManager::GetInstance( void )
{
	if( s_pInstance == nullptr )
		s_pInstance = new CEntityManager;

	return s_pInstance;
}
/////////////////////////////////////////////////////////////////////////////
// Function		: DeleteInstance
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
void CEntityManager::DeleteInstance( void )
{
	delete s_pInstance;
	s_pInstance = nullptr;
}
/////////////////////////////////////////////////////////////////////////////
// Function		: AddEntity
// Parameters	: ptr - The Enitity to add to the vector.
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
void CEntityManager::AddEntity( CMovingObject* ptr )
{
	if(ptr != nullptr && m_bIterating == false)
		m_BaseUnits.push_back(ptr);
}
/////////////////////////////////////////////////////////////////////////////
// Function		: AddEntity
// Parameters	: ptr - The Enitity to add to the vector.
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
void CEntityManager::AddEntity( CPlayerShip* ptr )
{
	if(ptr != nullptr && m_bIterating == false)
		m_PlayerShips.push_back(ptr);
}
/////////////////////////////////////////////////////////////////////////////
// Function		: AddEntity
// Parameters	: ptr - The Enitity to add to the vector.
// Notes		: 
/////////////////////////////////////////////////////////////////////////////

void CEntityManager::AddEntity( CBarricade* ptr )
{
	if(ptr != nullptr && m_bIterating == false)

		m_Barricades.push_back(ptr);
}
/////////////////////////////////////////////////////////////////////////////
// Function		: AddEntity
// Parameters	: ptr - The Enitity to add to the vector.
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
void CEntityManager::AddEntity( CBase* ptr )
{
	if(ptr != nullptr && m_bIterating == false)
		m_Bases.push_back(ptr);
}
/////////////////////////////////////////////////////////////////////////////
// Function		: AddEntity
// Parameters	: ptr - The Enitity to add to the vector.
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
void CEntityManager::AddEntity( CCrate* ptr )
{
	if(ptr != nullptr && m_bIterating == false)
		m_Crates.push_back(ptr);
}
/////////////////////////////////////////////////////////////////////////////
// Function		: RemoveEntity
// Parameters	: ptr - The Enitity to remove from the vector.
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
void CEntityManager::RemoveEntity( CMovingObject* ptr )
{
	if(ptr != nullptr && m_bIterating == false)
	{
		for (vector<CMovingObject*>::iterator iter = m_BaseUnits.begin();
			iter != m_BaseUnits.end(); ++iter)
		{
			if( (*iter) == ptr )
			{
				m_BaseUnits.erase( iter );
				delete ptr;
				break;
			}
		}
	}
}
/////////////////////////////////////////////////////////////////////////////
// Function		: RemoveEntity
// Parameters	: ptr - The Enitity to remove from the vector.
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
void CEntityManager::RemoveEntity( CPlayerShip* ptr )
{
	if(ptr != nullptr && m_bIterating == false)
	{
		for (vector<CPlayerShip*>::iterator iter = m_PlayerShips.begin();
			iter != m_PlayerShips.end(); ++iter)
		{
			if( (*iter) == ptr )
			{
				m_PlayerShips.erase( iter );
				delete ptr;
				break;
			}
		}
	}
}
/////////////////////////////////////////////////////////////////////////////
// Function		: RemoveEntity
// Parameters	: ptr - The Enitity to remove from the vector.
// Notes		: 
/////////////////////////////////////////////////////////////////////////////

void CEntityManager::RemoveEntity( CBarricade* ptr )
{
	if(ptr != nullptr && m_bIterating == false)
	{

		for (vector<CBarricade*>::iterator iter = m_Barricades.begin();
			iter != m_Barricades.end(); ++iter)
		{
			if( (*iter) == ptr )
			{

				m_Barricades.erase( iter );
				delete ptr;
				break;
			}
		}
	}
}
/////////////////////////////////////////////////////////////////////////////
// Function		: RemoveEntity
// Parameters	: ptr - The Enitity to remove from the vector.
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
void CEntityManager::RemoveEntity( CBase* ptr )
{
	if(ptr != nullptr && m_bIterating == false)
	{
		for (vector<CBase*>::iterator iter = m_Bases.begin();
			iter != m_Bases.end(); ++iter)
		{
			if( (*iter) == ptr )
			{
				m_Bases.erase( iter );
				delete ptr;
				break;
			}
		}
	}
}
/////////////////////////////////////////////////////////////////////////////
// Function		: RemoveEntity
// Parameters	: ptr - The Enitity to remove from the vector.
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
void CEntityManager::RemoveEntity( CCrate* ptr )
{
	if(ptr != nullptr && m_bIterating == false)
	{
		for (vector<CCrate*>::iterator iter = m_Crates.begin();
			iter != m_Crates.end(); ++iter)
		{
			if( (*iter) == ptr )
			{
				m_Crates.erase( iter );
				delete ptr;
				break;
			}
		}
	}
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Update
// Parameters	: dT - Change in time since last update.
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
void CEntityManager::Update(float dT)
{

	for (unsigned int i = 0; i < m_Barricades.size(); i++)
		m_Barricades[i]->Update(dT);

	for (unsigned int i = 0; i < m_Bases.size(); i++)
		m_Bases[i]->Update(dT);

	for (unsigned int i = 0; i < m_BaseUnits.size(); i++)
	{
		if(m_BaseUnits[i]->GetType() == SHIELD)
			dynamic_cast<CShielder*>(m_BaseUnits[i])->Update(dT);
		else
			m_BaseUnits[i]->Update(dT);
	}

	for (unsigned int i = 0; i < m_PlayerShips.size(); i++)
	{
		m_PlayerShips[i]->Update(dT);
		m_PlayerShips[i]->ThrowDeath();
	}

	for (unsigned int i = 0; i < m_Crates.size(); i++)
		m_Crates[i]->Update(dT);

	CheckCollision();
}
/////////////////////////////////////////////////////////////////////////////
// Function		: CheckCollision
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
void CEntityManager::CheckCollision(){
	bool IfHit = false;

	for(unsigned int bullet = 0; bullet < m_BaseUnits.size(); ++bullet)
	{
		if( m_BaseUnits[bullet]->GetType() == EntType::BULLET )
		{
			CBullet* pBullet = dynamic_cast<CBullet*>(m_BaseUnits[bullet]);
			for(unsigned int hit = 0; hit < m_BaseUnits.size(); ++hit)
			{
				if( pBullet->CheckCollision(m_BaseUnits[hit]) && m_BaseUnits[hit]->GetType() != EntType::BULLET && m_BaseUnits[hit]->GetType() != EntType::MISSILE
					&& m_BaseUnits[hit] != pBullet->GetOwner() && m_BaseUnits[hit]->GetTeam() != pBullet->GetOwner()->GetTeam() )
				{
					CBaseUnit* unit = dynamic_cast<CBaseUnit*>(m_BaseUnits[hit]);

					if( unit->GetPath() == pBullet->GetPath() || pBullet->GetOwner()->GetType() == BOSS )
					{
						if( unit->GetType() == BOSS )
							dynamic_cast<CBoss*>(unit)->PushBack( pBullet->GetDamage() );
						else
							unit->SetHealth( unit->GetHealth() - pBullet->GetDamage() );
						CEmitter *damage = new CEmitter();
						if(unit->GetType() == EntType::SHIELD || unit->GetType() == EntType::HELI || unit->GetType() == EntType::MECH){
							damage->LoadXML("HitMetalPartEffect.xml");
						}
						else if(unit->GetType() == EntType::ROCKET || unit->GetType() == EntType::SHOOT){
							damage->LoadXML("HitMeatPartEffect.xml");
						}
						damage->SetPos(pBullet->GetPosX(), pBullet->GetPosY());
						if(pBullet->GetTeam() == 1){
							damage->SetSVel(-damage->GetStVelX(), damage->GetStVelY());
							damage->SetEVel(-damage->GetEndVelX(), damage->GetEndVelY());
						}
						CParticleManager::GetInstance()->AddEmitter(damage);
						
						RemoveEntity(pBullet);

						if( unit->GetHealth() <= 0 )
						{
							CSGD_XAudio2::GetInstance()->SFXPlaySound(unit->GetDeathID());
							if(unit->GetTeam() == 1)
							{
								switch(pBullet->GetOwner()->GetType())
								{
								case SHOOT:
									if (CLevelManager::GetInstance()->GetCurrLevel()->GetLevelP1(0) < 5.0f)
									{
											CLevelManager::GetInstance()->GetCurrLevel()->IncreaseLevelP1(0);
									}
									break;

								case SHIELD:
									if (CLevelManager::GetInstance()->GetCurrLevel()->GetLevelP1(1) < 5.0f)
									{
										CLevelManager::GetInstance()->GetCurrLevel()->IncreaseLevelP1(1);
									}	
									break;

								case ROCKET:
									if (CLevelManager::GetInstance()->GetCurrLevel()->GetLevelP1(2) < 5.0f)
										CLevelManager::GetInstance()->GetCurrLevel()->IncreaseLevelP1(2);
									break;

								case MECH:
									if (CLevelManager::GetInstance()->GetCurrLevel()->GetLevelP1(3) < 5.0f)
										CLevelManager::GetInstance()->GetCurrLevel()->IncreaseLevelP1(3);
									break;

								case HELI:
									if (CLevelManager::GetInstance()->GetCurrLevel()->GetLevelP1(4) < 5.0f)
										CLevelManager::GetInstance()->GetCurrLevel()->IncreaseLevelP1(4);
									break;

								default:
									break;

								}
								CLevelManager::GetInstance()->GetCurrLevel()->GetTeam1()->RemoveUnit(unit->GetPath(), unit);
}

							else if( CLevelManager::GetInstance()->GetCurrLevel()->m_bBossLevel == false )
							{
																switch(pBullet->GetOwner()->GetType())
								{
								case SHOOT:
									if (CLevelManager::GetInstance()->GetCurrLevel()->GetLevelP2(0) < 5.0f)
									{
											CLevelManager::GetInstance()->GetCurrLevel()->IncreaseLevelP2(0);
									}
									break;

								case SHIELD:
									if (CLevelManager::GetInstance()->GetCurrLevel()->GetLevelP2(1) < 5.0f)
									{
										CLevelManager::GetInstance()->GetCurrLevel()->IncreaseLevelP2(1);
									}	
									break;

								case ROCKET:
									if (CLevelManager::GetInstance()->GetCurrLevel()->GetLevelP2(2) < 5.0f)
										CLevelManager::GetInstance()->GetCurrLevel()->IncreaseLevelP2(2);
									break;

								case MECH:
									if (CLevelManager::GetInstance()->GetCurrLevel()->GetLevelP2(3) < 5.0f)
										CLevelManager::GetInstance()->GetCurrLevel()->IncreaseLevelP2(3);
									break;

								case HELI:
									if (CLevelManager::GetInstance()->GetCurrLevel()->GetLevelP2(4) < 5.0f)
										CLevelManager::GetInstance()->GetCurrLevel()->IncreaseLevelP2(4);
									break;

								default:
									break;

								}
								CLevelManager::GetInstance()->GetCurrLevel()->GetTeam2()->RemoveUnit(unit->GetPath(), unit);
							}
							RemoveEntity(unit);
						}
						IfHit = true;
						break;
					}
				}
			}

			if( IfHit == false )
			{
				for(unsigned int hit = 0; hit < m_PlayerShips.size(); ++hit)
				{
					if( pBullet->CheckCollision(m_PlayerShips[hit]->GetShip()) && m_PlayerShips[hit] != pBullet->GetOwner() )
					{
						m_PlayerShips[hit]->SetHealth( m_PlayerShips[hit]->GetHealth() - pBullet->GetDamage() );
						RemoveEntity(pBullet);
						IfHit = true;
						break;
					}
				}
			}

			if( IfHit == false )
			{
				for( unsigned int base = 0; base < m_Bases.size(); ++base )
				{
					// loop through generators
					CDefenseStruct* generator;
					for(unsigned int gen = 0; gen < m_Bases[base]->GetShield()->GetGenerators().size(); ++gen)
					{
						generator = m_Bases[base]->GetShield()->GetGenerators()[gen];
						if( pBullet->CheckCollision(generator) && pBullet->GetOwner()->GetTeam() != m_Bases[base]->GetTeam() )
						{
							generator->SetHealth( generator->GetHealth() - pBullet->GetDamage() );
							CSGD_XAudio2::GetInstance()->SFXPlaySound(generator->GetHit());
							CEmitter *emit = new CEmitter();
							emit->LoadXML("HitGenPartEffect.xml");
							emit->SetPos(pBullet->GetPosX(), pBullet->GetPosY());
							if(pBullet->GetTeam() == 1){
								emit->SetSVel(-emit->GetStVelX(), emit->GetStVelY());
								emit->SetEVel(-emit->GetEndVelX(), emit->GetEndVelY());
							}
							CParticleManager::GetInstance()->AddEmitter(emit);
							RemoveEntity(pBullet);
							if( generator->GetHealth() <= 0 )
							{
								m_Bases[base]->GetShield()->RemoveGenerator(gen);
								// CHANGE - hard-coded shield health decrement
								m_Bases[base]->GetShield()->SetHealth( m_Bases[base]->GetShield()->GetHealth() - 34 );
								CSGD_XAudio2::GetInstance()->SFXPlaySound(m_Bases[base]->GetShield()->GetHit());
								// No More Generators
								if( m_Bases[base]->GetShield()->GetHealth() <= 0 )
								{
									CSGD_XAudio2::GetInstance()->SFXPlaySound(m_Bases[base]->GetShield()->GetDeath());
									m_Bases[base]->RemoveShield();
								}
							}
							IfHit = true;
							break;
						}
					}

					if( IfHit )
						break;

					if( pBullet->CheckCollision(m_Bases[base]) && pBullet->GetOwner()->GetTeam() != m_Bases[base]->GetTeam() )
					{
						if(m_Bases[base]->GetShield()->IsActive() == false)
						{
							CSGD_XAudio2::GetInstance()->SFXPlaySound(m_Bases[base]->GetHit());
							m_Bases[base]->SetHealth( m_Bases[base]->GetHealth() - pBullet->GetDamage() );
							CEmitter *emit = new CEmitter();
							emit->LoadXML("HitBasePartEffect.xml");
							emit->SetPos(pBullet->GetPosX(), pBullet->GetPosY());
							if(pBullet->GetTeam() == 1){
								emit->SetSVel(-emit->GetStVelX(), emit->GetStVelY());
								emit->SetEVel(-emit->GetEndVelX(), emit->GetEndVelY());
							}
							CParticleManager::GetInstance()->AddEmitter(emit);
						}
						else
						{
							int RandChance = rand() % 5;
							// CHANGE - hard-coded decrement of bullet damage
							if(RandChance == 0){
								m_Bases[base]->SetHealth(m_Bases[base]->GetHealth() - pBullet->GetDamage() * 0.1f);
								CEmitter *emit = new CEmitter();
								emit->LoadXML("HitBasePartEffect.xml");
								emit->SetPos(pBullet->GetPosX(), pBullet->GetPosY());
								if(pBullet->GetTeam() == 1){
									emit->SetSVel(-emit->GetStVelX(), emit->GetStVelY());
									emit->SetEVel(-emit->GetEndVelX(), emit->GetEndVelY());
								}
								CParticleManager::GetInstance()->AddEmitter(emit);
							}
							else{
								CEmitter *emit = new CEmitter();
								emit->LoadXML("HitShieldPartEffect.xml");
								emit->SetPos(pBullet->GetPosX(), pBullet->GetPosY());
								if(pBullet->GetTeam() == 1){
									emit->SetSVel(-emit->GetStVelX(), emit->GetStVelY());
									emit->SetEVel(-emit->GetEndVelX(), emit->GetEndVelY());
								}
								CParticleManager::GetInstance()->AddEmitter(emit);
							}
							m_Bases[base]->GetShield()->SetHealth( m_Bases[base]->GetShield()->GetHealth() - pBullet->GetDamage() * 0.1f );
						}

						if( m_Bases[base]->GetShield()->GetHealth() <= 0 )
							m_Bases[base]->RemoveShield();

						RemoveEntity(pBullet);
						if( m_Bases[base]->GetHealth() <= 0 )
						{
							CSGD_XAudio2::GetInstance()->SFXPlaySound(m_Bases[base]->GetDeath());
							if(m_Bases[base]->GetTeam() == 1)
							{
								CSGD_XAudio2::GetInstance()->MusicStopSong(CGame::GetInstance()->GetMusicID());
								CSGD_XAudio2::GetInstance()->MusicPlaySong(m_Bases[base]->GetLose());
								CMainMenuState::GetInstance()->SetWinOrLose(true);
							}
							else
							{
								CSGD_XAudio2::GetInstance()->MusicStopSong(CGame::GetInstance()->GetMusicID());
								CSGD_XAudio2::GetInstance()->MusicPlaySong(m_Bases[base]->GetWin());
								CMainMenuState::GetInstance()->SetWinOrLose(false);
							}

							RemoveEntity(m_Bases[base]);
							CMessage* msg = new CMessage();
							msg->SetMessageID(MSG_END_GAME);
							CGame::GetInstance()->GetMessageSystem()->GetInstance()->SendNewMessage(msg);
						}
						IfHit = true;
						break;
					}
				}
			}

			if( IfHit == false )
			{
				for( unsigned int wall = 0; wall < m_Barricades.size(); ++wall )
				{
					// loop through barricades
					CBarricade* barr = m_Barricades[wall];
					if( pBullet->CheckCollision(barr) && pBullet->GetOwner()->GetTeam() != barr->GetTeam() )
					{
						barr->SetHealth( barr->GetHealth() - pBullet->GetDamage() );
						if( barr->GetHealth() <= 0 )
						{
							if( barr->GetTeam() == 1 )
								CLevelManager::GetInstance()->GetCurrLevel()->GetTeam1()->RemoveBarricade( pBullet->GetPath(), barr );
							else
								CLevelManager::GetInstance()->GetCurrLevel()->GetTeam2()->RemoveBarricade( pBullet->GetPath(), barr );

							RemoveEntity( barr );
						}
						RemoveEntity(pBullet);
						IfHit = true;
						break;
					}
				}
			}
		}
	}
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Render
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
void CEntityManager::Render(void)
{
	for (unsigned int i = 0; i < m_Bases.size(); i++)
		m_Bases[i]->Render();

	for (unsigned int i = 0; i < m_PlayerShips.size(); i++)
		m_PlayerShips[i]->Render();

	vector<CMovingObject*> Layer0;
	vector<CMovingObject*> Layer1;
	vector<CMovingObject*> Layer2;
	vector<CMovingObject*> Layer3;

	// Check what layer things are on
	for (unsigned int i = 0; i < m_BaseUnits.size(); i++)
	{
		switch (m_BaseUnits[i]->GetPath())
		{
		case 0:
			Layer0.push_back(m_BaseUnits[i]);
			break;
		case 1:
			Layer1.push_back(m_BaseUnits[i]);
			break;
		case 2:
			Layer2.push_back(m_BaseUnits[i]);
			break;
		case 3:
			Layer3.push_back(m_BaseUnits[i]);
			break;
		default:
			//assert( m_BaseUnits[i]->GetPath() < 3 && m_BaseUnits[i]->GetPath() > 0 && "Object has invalid layer - EntityManager::Render" );
			break;
		};
	}

	for (unsigned int i = 0; i < Layer0.size(); i++)
		Layer0[i]->Render();

	for (unsigned int i = 0; i < Layer1.size(); i++)
		Layer1[i]->Render();

	for (unsigned int i = 0; i < Layer2.size(); i++)
		Layer2[i]->Render();

	for (unsigned int i = 0; i < Layer3.size(); i++)
		Layer3[i]->Render();


	for (unsigned int i = 0; i < m_Barricades.size(); i++)
		m_Barricades[i]->Render();

	for (unsigned int i = 0; i < m_Crates.size(); i++)
		m_Crates[i]->Render();
}
/////////////////////////////////////////////////////////////////////////////
// Function		: Clear
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
void CEntityManager::Clear(void)
{

	for (unsigned int i = 0; i < m_Barricades.size(); i++)
		delete m_Barricades[i];

	for (unsigned int i = 0; i < m_Bases.size(); i++)
		delete m_Bases[i];

	for (unsigned int i = 0; i < m_BaseUnits.size(); i++)
		delete m_BaseUnits[i];

	for (unsigned int i = 0; i < m_PlayerShips.size(); i++)
		delete m_PlayerShips[i];

	for (unsigned int i = 0; i < m_Crates.size(); i++)
		delete m_Crates[i];


	m_Barricades.clear();
	m_Bases.clear();
	m_BaseUnits.clear();
	m_PlayerShips.clear();
	m_Crates.clear();
}
/////////////////////////////////////////////////////////////////////////////
// Function		: FindUnit
// Notes		: 
/////////////////////////////////////////////////////////////////////////////
int CEntityManager::FindUnit( CMovingObject* unit)
{
	for (unsigned int i = 0; i < m_BaseUnits.size() ; i++)
	{
		if(m_BaseUnits[i] == unit)
			return i;
	}

	return 0;
}

void CEntityManager::HandleEvent( Event* pEvent ){
	// type of event
	if( pEvent->GetEventIDtype() == "MISSILE" )
	{
		CBullet* pMissile = (CBullet*)(pEvent->GetSender());
		for( unsigned int i = 0; i < m_BaseUnits.size(); )
		{
			if( m_BaseUnits[i]->GetType() != BULLET && m_BaseUnits[i]->GetType() != MISSILE )
			{
				CBaseUnit* pUnit = dynamic_cast<CBaseUnit*>(m_BaseUnits[i]);

				// Check if it is in range of Missile
				// CHANGE - hard-coded missile radius
				if( pUnit->DistanceBetween(pUnit->GetPos(), pMissile->GetPos()) < 200 )
				{
					pUnit->SetHealth( pUnit->GetHealth() - pMissile->GetDamage() );
					// Is it dead?
					if( pUnit->GetHealth() <= 0 )
						RemoveEntity( pUnit );
					else
						++i;
				}
				else
					++i;
			}
			else
				++i;
		}
		RemoveEntity( pMissile );
	}
}
